"""
Based on: http://shiftlock.wordpress.com/2010/10/26/winpcap-network-packet-logger-in-python/ By prekageo
"""

from optparse import OptionParser
import ctypes, re, sys
import winpcapy

def data_to_num(data, offset, len):
    """ Convert data from an array into an integer (big-endian). """
    num = 0
    for i in xrange(len):
        num <<= 8
        num |= data[offset+i]
    return num

def ip_to_str(data, offset):
    """ Convert data from an array into a human-readable IP address. """
    return '%d.%d.%d.%d' % tuple(data[offset:offset+4])

class PacketCapture(object):
    def _packet_handler(self, param,header,pkt_data):
        """ Handle an intercepted network packet. """
        ether_type = data_to_num(pkt_data, 12, 2)
        if ether_type != 0x0800:
            # if not IP packet return
            return
    
        ip_header_length = (pkt_data[14] & 15) * 4
        ip_length = data_to_num(pkt_data, 16, 2)
        ip_protocol = pkt_data[23]
        source_ip = ip_to_str(pkt_data, 26)
        dest_ip = ip_to_str(pkt_data, 30)
        ip_data_ofs = 14 + ip_header_length
        if ip_protocol != 6:
            # if not TCP packet return
            return
    
        tcp = {}
        tcp_source_port = data_to_num(pkt_data, ip_data_ofs, 2)
        tcp_dest_port = data_to_num(pkt_data, ip_data_ofs+2, 2)
        tcp_seq = data_to_num(pkt_data, ip_data_ofs+4, 4)
        tcp_ack = data_to_num(pkt_data, ip_data_ofs+8, 4)
        tcp_header_length = (pkt_data[ip_data_ofs+12]>>4)*4
        tcp_data_ofs = ip_data_ofs+tcp_header_length
        tcp_flags = pkt_data[ip_data_ofs+13]
        tcp_flag_fin = (tcp_flags & 1) == 1
        tcp_flag_syn = (tcp_flags & 2) == 2
        tcp_flag_ack = (tcp_flags & 16) == 16
        tcp_data_len = ip_length - ip_header_length - tcp_header_length
        tcp_data = []
        for i in xrange(tcp_data_len):
            tcp_data.append(chr(pkt_data[tcp_data_ofs+i]))
        tcp_data = ''.join(tcp_data)
        raw_packet = []
        for i in xrange(ip_length):
            raw_packet.append(chr(pkt_data[i]))
        raw_packet = ''.join(raw_packet)
    
        tcp_packet = {}
        tcp_packet['source_ip'] = source_ip
        tcp_packet['source_port'] = tcp_source_port
        tcp_packet['dest_ip'] = dest_ip
        tcp_packet['dest_port'] = tcp_dest_port
        tcp_packet['flag'] = {}
        tcp_packet['flag']['fin'] = tcp_flag_fin
        tcp_packet['flag']['syn'] = tcp_flag_syn
        tcp_packet['flag']['ack'] = tcp_flag_ack
        tcp_packet['data'] = tcp_data
        tcp_packet['raw'] = raw_packet
        self.handle_tcp_packet(tcp_packet)
    
    
    def handle_tcp_packet(self, tcp_packet):
        """ Handle an intercepted TCP packet. """
        if not self.filter_tcp_packet(tcp_packet):
            return
    
    
    def start_capture(self, capture_iface):
        """ Start the network capture on the specified interface. """
        PHAND = ctypes.CFUNCTYPE(None,ctypes.POINTER(ctypes.c_ubyte),
                                 ctypes.POINTER(winpcapy.pcap_pkthdr),ctypes.POINTER(ctypes.c_ubyte))
        errbuf = ctypes.create_string_buffer(winpcapy.PCAP_ERRBUF_SIZE)
    
        interfaces = get_interfaces()
        interface = interfaces[capture_iface - 1]
        adhandle = winpcapy.pcap_open_live(interface.name,65536,
                                           winpcapy.PCAP_OPENFLAG_PROMISCUOUS,1000,errbuf)
        print("Capturing on %s...\n" % (interface.description,))
        winpcapy.pcap_loop(adhandle, -1, PHAND(self._packet_handler), None)
        winpcapy.pcap_close(adhandle)
    
    def filter_tcp_packet(self, tcp_packet):
        """
        Return True if we want the packet
        """
        return False

    @staticmethod
    def get_interfaces():
        """ Get a list of available network interfaces. """
        errbuf = ctypes.create_string_buffer(winpcapy.PCAP_ERRBUF_SIZE)
    
        alldevs = ctypes.POINTER(winpcapy.pcap_if_t)()
        winpcapy.pcap_findalldevs(ctypes.byref(alldevs), errbuf)
        device = alldevs.contents
        interfaces = []
        while True:
            interfaces.append(device)
            if not device.next:
                break
            device = device.next.contents
        return interfaces
    
    @staticmethod
    def print_interfaces():
        """ Print the list of available network interfaces. """
        interfaces = get_interfaces()
    
        counter = 1
        print 'Available interfaces'
        for inteface in interfaces:
            print '%2d. %s' % (counter, inteface.description)
            counter += 1